package com.gopay.common.proccode.impl;

import java.util.List;

import javax.annotation.Resource;

import com.gopay.common.constants.proccode.ProcCodeEnum;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.gopay.common.domain.dic.DicProcCodeGopay;
import com.gopay.common.domain.proccode.ProcCodeInfo;
import com.gopay.common.proccode.DicProcCodeService;
import com.gopay.common.proccode.dao.IDicProcCodeDAO;

/**
 * 处理码解析
 * 
 * @dicPrcoCodeServiceImpl.java
 * @author liujy
 * @2012-12-21 下午4:21:05 www.gopay.com.cn Inc.All rights reserved.
 */
@Service("dicProcCodeService")
public class DicProcCodeServiceImpl implements DicProcCodeService {

    private final static Logger logger = LoggerFactory.getLogger("PROC_CODE_LOG");
    
    @Resource(name="dicProcCodeDAO")
    private IDicProcCodeDAO dicPrcoCodeDAO;

    @Override
    public ProcCodeInfo getProcCodeInfo(String procCode) {
        return getProcCodeInfo(procCode, "ZH_CN");
    }

    @Override
    public ProcCodeInfo getProcCodeInfo(String procCode, String language) {
        return switchProcCode(procCode, "0", language);
    }

    @Override
    public ProcCodeInfo switchProcCode(String procCode, String orderType) {
        return switchProcCode(procCode, orderType, "ZH_CN");
    }

    @Override
    public ProcCodeInfo switchProcCode(String procCode, String orderType, String language) {

        logger.info("处理码解析请求procCode：" + procCode + "|orderType：" + orderType + "|language：" + language);

        ProcCodeInfo info = initProcCodeInfo(procCode);
        if (false == checkErrorCode(procCode)) {
            logger.error("处理码格式检查失败procCode：" + procCode);
            setRespCodeInfo(info, ERROR_CODE_NOT_FOUND, "");
            return info;
        }

        // 格式化交易类型
        orderType = formatOrderType(orderType);
        // 格式化语言
        language = formatLanguage(language);

        /*从枚举中获取描述 added by wangdong at 2018-06-15 start*/
        ProcCodeEnum procCodeEnum = ProcCodeEnum.procCodeEnumMap.get(procCode);
        String codeDesc;
        if (procCodeEnum!=null){
            codeDesc = procCodeEnum.getMsg();
        }else{
            logger.info("【获取错误描述】内存中（ProcCodeEnum）未获取，开始查询数据库。（枚举中未穷举，请将该异常码维护到ProcCodeEnum中：{}）",procCode);
            // 查询处理码出表
            DicProcCodeGopay dicProcCode = getProcCodeGopay(procCode);
            codeDesc = dicProcCode.getCodeDesc();
        }
        /*从枚举中获取描述 added by wangdong at 2018-06-15 end*/

        // 如果是0不需要转码，直接返回即可
        if (StringUtils.equals("0", orderType)) {
            printEFProcCode(procCode);
            setRespCodeInfo(info, procCode, codeDesc);
            return info;
        }
        
        // 需要转码，查询商户通知处理码参数表
        String respCode = getProcCodeMer(procCode, orderType);
        if (StringUtils.isEmpty(respCode)){
            // 关联记录有误，返回请求处理码
            logger.error("处理码关联查询记录不存在或重复procCode：" + procCode + "|orderType：" + orderType);
            setRespCodeInfo(info, procCode, codeDesc);
        }else{
            setRespCodeInfo(info, respCode, codeDesc);
        }
        
        printEFProcCode(procCode);
        return info;
    }
    
   /**
    * 错误码日志输出
    * @param procCode
    */
    private void printEFProcCode(String procCode){
        if(procCode.substring(3, 5).equals("E9")
                || procCode.substring(3, 4).equals("F")){
            logger.error("E9或F级别错误码procCode：" + procCode);
        }
    }
    
    /**
     * 
     * @param procCode
     * @return
     */
    private DicProcCodeGopay getProcCodeGopay(String procCode){
        DicProcCodeGopay temp = new DicProcCodeGopay();
        List<?> list = dicPrcoCodeDAO.getProcCodeGopayById(procCode);
        if (list == null || list.size() < 1) {
            // 记录不存在特定码
            logger.error("处理码主参数表记录不存在procCode：" + procCode);
            temp.setGopayProcCode(ERROR_CODE_NOT_FOUND);
        }else if (list.size() > 1) {
            // 记录重复特定码
            logger.error("处理码主参数表记录重复procCode：" + procCode);
            temp.setGopayProcCode(ERROR_CODE_DUPLICATE);
        }else{
            temp = (DicProcCodeGopay) list.get(0);
        }
        return temp;
    }

    /**
     * 查询转码参数
     * 
     * @param procCode
     * @param orderType
     * @return
     */
    private String getProcCodeMer(String procCode, String orderType) {
        if (StringUtils.equals("1", orderType)) {
            return dicPrcoCodeDAO.getProcCodeMerGen(procCode);
        } else if (StringUtils.equals("3", orderType)) {
            return dicPrcoCodeDAO.getProcCodeMerBT(procCode);
        }
        return null;
    }

    /**
     * 
     * @param info
     * @param respCode
     * @param mesage
     */
    private void setRespCodeInfo(ProcCodeInfo info, String respCode, String mesage) {
        info.setRespCode(respCode);
        info.setMessage(mesage);
    }
    
    /**
     * 默认：0
     * 
     * @param orderType
     * @return
     */
    private String formatOrderType(String orderType) {
        if (StringUtils.isEmpty(orderType)) {
            return "0";
        }
        orderType = orderType.trim();
        if (StringUtils.equals("0", orderType)) {
            return orderType;
        }
        if (StringUtils.equals("1", orderType)) {
            return orderType;
        }
        if (StringUtils.equals("3", orderType)) {
            return orderType;
        }
        return "0";
    }

    /**
     * 默认：ZH_CN
     * 
     * @param language
     * @return
     */
    private String formatLanguage(String language) {
        if (StringUtils.isEmpty(language) || !language.matches("^[A-Z]{2}_[A-Z]{2}$")) {
            return "ZH_CN";
        }
        return language.trim();
    }

    /**
     * 
     * @param errorCode
     * @return
     */
    private boolean checkErrorCode(String errorCode) {
        if (StringUtils.isEmpty(errorCode)) {
            return false;
        }
        // 格式：三位数字+[EFGSW]+一位数字或字母+三位数字  dak.wang  20160818 加了G用于风控异常
        if (!errorCode.matches("^[0-9]{3}[EFGSW]{1}[0-9A-Z]{1}[0-9]{3}$")) {
            return false;
        }
        return true;
    }

    /**
     * 
     * @param errorCode
     * @return
     */
    private ProcCodeInfo initProcCodeInfo(String procCode) {
        ProcCodeInfo info = new ProcCodeInfo();
        info.setRespCode(procCode);
        info.setGopayProcCode(procCode);
        info.setMessage("");
        return info;
    }
}
